package com.wutong.resonance.model.plugin.visual;

import com.wutong.resonance.model.audio.AudioData;
import com.wutong.resonance.model.audio.AudioParams;
import com.wutong.resonance.model.util.AudioUtil;
import javafx.application.Platform;
import javafx.scene.paint.Color;
import javafx.scene.layout.Pane;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.jtransforms.fft.DoubleFFT_1D;

import java.util.Arrays;

/**
 * 实时频率振幅显示器
 *
 * @author wutong
 */
@Slf4j
@Setter
@Getter
public class FrequencyAmplitudeVisualizer extends VisualPlugin {

    private Color barColor = Color.CYAN;
    private double maxAmplitude = 1.0;

    // 缓存频率值映射
    private double[] frequencyBins;
    // 频率分辨率
    private double freqStep;
    // 频谱数据缓存
    private double[] amplitudes;

    public FrequencyAmplitudeVisualizer(AudioParams audioParams,
                                        int initialFrameSize,
                                        Pane parent) {
        super("实时频率振幅显示器", "实时频率振幅显示器", parent, audioParams);
        this.initialFrameSize = initialFrameSize;
        this.frameSize = initialFrameSize;
        // 获取采样率
        this.sampleRate = audioParams.getSampleRate();
        // 计算更新间隔 (毫秒)
        this.updateRate = (double) frameSize / sampleRate * 1000;
    }

    @Override
    protected void initPlugin(AudioParams audioParams) {
        super.initPlugin(audioParams);
        // 监听画布宽度变化，动态调整 frameSize
        canvas.widthProperty().addListener((obs, oldVal, newVal) -> adjustFrameSize(newVal.doubleValue()));
        // 初始调整
        adjustFrameSize(canvas.getWidth());
        freqStep = (double) sampleRate / initialFrameSize;

        frequencyBins = new double[initialFrameSize / 2];
        for (int i = 0; i < frequencyBins.length; i++) {
            frequencyBins[i] = i * freqStep;
        }
        initTimeline();
    }

    private double[] calculateFullSpectrum(double[][] stereoData) {
        DoubleFFT_1D fft = new DoubleFFT_1D(stereoData[0].length);
        // 对左右声道各执行一次FFT
        fft.realForward(stereoData[0]);
        fft.realForward(stereoData[1]);

        double[] spectrum = new double[stereoData[0].length / 2];
        for (int i = 0; i < spectrum.length; i++) {
            spectrum[i] = (AudioUtil.calculateMagnitude(stereoData[0], i)
                    + AudioUtil.calculateMagnitude(stereoData[1], i)) / 2;
        }
        return spectrum;
    }

    @Override
    protected void draw(AudioData audioData) {
        double[][] inputData = audioData.getDoubleMatrix();
        amplitudes = calculateFullSpectrum(inputData);

        double canvasWidth = canvas.getWidth();
        double canvasHeight = canvas.getHeight();
        double xStep = canvasWidth / amplitudes.length;

        double maxAmplitude = Arrays.stream(amplitudes).max().orElse(1);

        Platform.runLater(() -> {
            initDraw();
            gc.setStroke(foregroundColor);
            gc.beginPath();
            gc.moveTo(0, canvasHeight);

            for (int i = 0; i < amplitudes.length; i++) {
                // 调整幅度计算：将原点移动到画布底部，使用对数缩放[^1]
                double normalized = (amplitudes[i] / maxAmplitude) * 0.9 + 0.01;
                double y = canvasHeight * (1 - normalized);

                if (i == 0) {
                    gc.moveTo(0, y);
                } else {
                    double x = xStep * i;
                    gc.lineTo(x, y);
                }
            }
            gc.stroke();
        });
    }

    @Override
    protected void redrawBackground() {
        gc.setFill(backgroundColor);
        gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());

        // 绘制标尺
        gc.setStroke(Color.GRAY);
        gc.setLineWidth(0.5);
        for (int i = 1; i < 5; i++) {
            double y = canvas.getHeight() * i / 5;
            gc.strokeLine(0, y, canvas.getWidth(), y);
        }
    }

    @Override
    protected void unloadPlugin() {
        super.unloadPlugin();
        if (timeline != null) {
            timeline.stop();
        }
    }

    /**
     * 插件视图
     *
     * @param parent 父容器
     *
     * @return 插件视图
     */
    @Override
    public Pane view(Pane parent) {
        return null;
    }
}
